import requests, time, dns.message, dns.query, re, sys, os
from urllib.parse import urlparse

from WSModule import WSModule
from WSThread import WSThread
from WSJob import WSJob
from WSResult import WSResult
from WSOption import WSOption
from WSOptionGeneratorDict import WSOptionGeneratorDict
from PreResult import PreResult
from Projects import Projects
from Domains import Domains

class Pre(WSModule):
    log_path = os.getcwd() + '/results/pre-result'
    #options = {"url": WSOption("url", "Target URL", "", True, ['-u', '--url'])} #TODO автодобавление нового домена?
    options = {
        "url": WSOption("url", "Target URL (from root)", "", True, ['-u', '--url']),
        "project": WSOption("project", "Target project", "", True, ['-p', '--project']),
        "domain": WSOption("domain", "Target domain", "", True, ['-d', '--domain'])
        #TODO http(s)
    }

    def __init__(self, kernel):
        super().__init__(kernel)
        for option_name in self.options:
            self.options[option_name].module = self

    def run(self):
        projects = Projects()
        project_id = projects.get_project_id(self.options['project'].value)
        if not project_id:
            exit(print("ERROR: Check project name or id. Project '{0}' not found.".format(self.options['project'].value)))

        domains = Domains()
        domain_id = domains.get_domain_id(project_id, self.options['domain'].value)
        if not domain_id:
            exit(print("ERROR: Check domain name or id. Domain '{0}' not found in project {1}."
                           .format(self.options['domain'].value, self.options['project'].value)))
        domain = domains.get_by_id(domain_id) #FIXME на нормальное имя исправить, может вообще в предыдущем методе всё сразу возвращать?

        url = "http://{0}{1}".format(domain['name'], self.options['url'].value)
        root_url = "{0}://{1}".format(urlparse(url).scheme, urlparse(url).hostname)

        result = PreResult()
        self.result = result

        if not self.check_url_valid(url):
            exit(print("URL '{0}' is not valid!\n".format(url)))

        result.put('ns', self.check_ns(urlparse(url).hostname))

        result.put('powered_by', self.check_powered_by(url))

        result.put('robots_txt', self.check_robots_txt(root_url))

        nf_res = self.check_404(root_url)
        result.put('nf', nf_res)

        dafs_dirs = self.check_dafs(root_url, 'dirs')\
                    if nf_res['dirs']\
                    else []
        result.put('dafs_dirs', dafs_dirs)

        dafs_files = self.check_dafs(root_url, 'files')\
                     if nf_res['files']\
                     else []
        result.put('dafs_files', dafs_files)

        result.put('title_and_encoding', self.check_title_and_encoding(root_url))

        result.put('headers', self.check_headers(root_url))

        result.put('svn', self.check_svn(root_url))

        print(str(result.as_string()))
    def help(self):
        return "Dafs help"

    def finished(self):
        return self.kernel.finished()

    def check_ns(self, domain): # TODO обрабатывать и по нс домена
        result_names = []

        ipRe = re.compile(r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})")
        nsRespRe = re.compile(r";ANSWER\s(?P<data>(.|\s)*)\s;AUTHORITY", re.M)

        for name in ['alpha', 'beta', 'gamma', 'dev',  'test', 'admin', 'control']:
            query = dns.message.make_query(name + '.' + domain, 'A')
            result = dns.query.tcp(query, '8.8.8.8', timeout=5)

            response = nsRespRe.search(result.to_text())
            if response is not None and ipRe.findall(response.group('data')):
                result_names.append(name + '.' + domain)
        return result_names

    def check_powered_by(self, url):
        r = requests.get(url)
        return re.findall("(powered by (?:.*)</)", r.text, re.I)

    def check_robots_txt(self, url):
        return url + "/robots.txt" \
                if requests.get(url + "/robots.txt").status_code != 404 \
                else None

    def check_dafs(self, url, type):
        result = []
        dict = open('pre-dafs-{0}.txt'.format(type), 'r').read().split("\n")
        for obj in dict:
            r = requests.get(url + obj)
            if r.status_code != 404:
                result.append("{0} {1}".format(r.status_code, url + obj))
        return result

    def check_404(self, url):
        result = {'dirs': False, 'files': False}

        r = requests.get(url + "/ergergergergegerger.php")
        if r.status_code == 404:
            result['files'] = True

        r = requests.get(url + "/ergergergergeg/")
        if r.status_code == 404:
            result['dirs'] = True

        return result

    def check_title_and_encoding(self, url):
        r = requests.get(url)
        title_pos = r.text.find("</title")
        if title_pos == -1:
            return False

        return bool(re.search('http-equiv=(.?)content-type(.?)', r.text, re.I))

    def check_headers(self, url):
        result = []
        r = requests.get(url)
        for header in r.headers:
            if header[:2].lower() == "x-" or header.lower() == 'server':
                result.append({'name': header, 'value': r.headers[header]})
        return result

    def check_svn(self, url):
        result = []
        for obj in ["/.svn/entries", "/.svn/wc.db", "/.svn/pristine/"]:
            r = requests.get(url + obj)
            if r.status_code != 404:
                result.append("{0} {1}".format(r.status_code, url + obj))
        return result

    def check_url_valid(self, url):
        tmp = urlparse(url)
        return (tmp.scheme and tmp.hostname)
